Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Int - two's complement #695

Merged
merged 88 commits into from
Nov 1, 2024
Merged

Conversation

erik-3milabs
Copy link

@erik-3milabs erik-3milabs commented Oct 16, 2024

Topic
Adding Int support (see #624/#700)

To decide on sign-and-magnitude (#694) vs. two's complement, I whipped up a quick two's complement implementation of the basic add/sub/mul/div operations. It's not perfect yet; I'm just curious to see how it compares to #694.

@erik-3milabs
Copy link
Author

erik-3milabs commented Oct 16, 2024

FYI: local benchmark results @ b23483d

wrapping ops/split_mul, 
I128xI128:      26.042 ns
I256xI256:      26.104 ns
I512xI512:      68.643 ns
I1024xI1024:   239.23  ns
I2048xI2048:   799.26  ns
I4096xI4096: 2.5102    µs

wrapping ops/div, full size
I256/I128:     351.69 ns
I512/I256:     554.63 ns
I1024/I512:  1.0663   µs
I2048/I1024: 2.5858   µs
I4096/I2048: 7.2846   µs

wrapping ops/add, 
I128+I128:     1.1915 ns
I256+I256:     2.4108 ns
I512+I512:    10.374  ns
I1024+I1024:  20.704  ns
I2048+I2048:  65.318  ns
I4096+I4096: 103.10   ns

wrapping ops/sub,
I128-I128:     1.1967 ns
I256-I256:     2.3008 ns
I512-I512:    10.985  ns
I1024-I1024:  32.041  ns
I2048-I2048:  70.872  ns
I4096-I4096: 128.86   ns

src/int.rs Outdated Show resolved Hide resolved
@erik-3milabs
Copy link
Author

erik-3milabs commented Oct 28, 2024

@tarcieri Re: proptest
Given the scope of this project, perhaps we should make a tracking PR/issue for these and other missing features?

@erik-3milabs
Copy link
Author

@tarcieri responded to your new and some old remarks. IIUC, all open discussions can be resolved except the one about encoding. I'll need some time to look into that. @lleoha I took the liberty to resolve some of the earlier remarks which I dealt with at the time to clean this PR up a bit.

@tarcieri
Copy link
Member

Given the scope of this project, perhaps we should make a tracking PR/issue for these and other missing features?

@erik-3milabs if you make such an issue yourself, you can manage the task list in the description. Otherwise you'll be blocked on me to do updates.

@erik-3milabs erik-3milabs mentioned this pull request Oct 29, 2024
7 tasks
@erik-3milabs
Copy link
Author

@lleoha I found your remark regarding neg in my inbox, but cannot find it anywhere in this thread for some reason, so I'm responding here.

You were right: the carry is non -zero iff self = Self::ZERO. I attempted to fix it in 7688826. Let me know what you think.

@tarcieri
Copy link
Member

You'll likely need to expand the nearly hundred comments in the middle of the thread, which is all the more reason to get this merged.

I'll try to do another pass soon.

@lleoha
Copy link

lleoha commented Oct 29, 2024

You were right: the carry is non -zero iff self = Self::ZERO. I attempted to fix it in 7688826. Let me know what you think.

@erik-3milabs The carry is non-zero also when Int::MIN is being negated, it's Word::ONE in this case. Or at least it should be, right?

@erik-3milabs
Copy link
Author

@lleoha IIRC, the negation map is $x \to (x \oplus 1111...1111) + 0000...0001$

Applying this to Int::MIN $= 1000...0000$ we get

$1000...0000 \oplus 1111...1111 = 0111...1111$
and
$0111...1111 + 0000...0001 = 1000...0000$ with a zero-carry.

What am I missing / what is your reasoning for saying the carry should equal 1?

@lleoha
Copy link

lleoha commented Oct 30, 2024

@lleoha IIRC, the negation map is x → ( x ⊕ 1111. . .1111 ) + 0000. . .0001

Applying this to Int::MIN = 1000. . .0000 we get

  1. . .0000 ⊕ 1111. . .1111 = 0111. . .1111 and 0111. . .1111 + 0000. . .0001 = 1000. . .0000 with a zero-carry.

What am I missing / what is your reasoning for saying the carry should equal 1?

Let's say the limb is 8 bit. So the min is -128. Then neg(-128) = 128, which can only be represented with two limbs in signed arithmetic.
Also with you logic !(-128) + 1 = 127 + 1. 127 + 1 overflows in signed arithmetics hence carry.

@erik-3milabs
Copy link
Author

Fourth attempt to write this.

I investigated this issue a bit, and I think I figured out we were both mistaken.

As it turns out, the devil is in the tiniest of details:

Also with you[r] logic !(-128) + 1 = 127 + 1. 127 + 1 overflows in signed arithmetics hence carry.

You are correct that that addition overflows, but I don't think your "hence" is correct. When operating on signed integers, the carry flag is irrelevant. Instead, the overflow flag is set. Source.

IMU, carrying_negation is simply not a thing in signed integer arithmetic. It should be overflowing_negation, where the overflow flag is raised when an addition overflows. This now also makes more sense: the overflow flag now tells you that negation fails.

For unsigned integers, the whole concept of "negation" is vague anyway. There, I decided to stick with "carrying_neg" and have it carry when "negating" zero, as that seems to make the most sense.

Thank you for persisting! Let me know what you think of the patch I wrote.

@lleoha
Copy link

lleoha commented Oct 31, 2024

You are correct that that addition overflows, but I don't think your "hence" is correct. When operating on signed integers, the carry flag is irrelevant. Instead, the overflow flag is set. Source.

IMU, carrying_negation is simply not a thing in signed integer arithmetic. It should be overflowing_negation, where the overflow flag is raised when an addition overflows. This now also makes more sense: the overflow flag now tells you that negation fails.

For unsigned integers, the whole concept of "negation" is vague anyway. There, I decided to stick with "carrying_neg" and have it carry when "negating" zero, as that seems to make the most sense.

Hi @erik-3milabs . I kind of agree, It boils down to the definition.
The CPUs don't have a notion of "signess" when doing addition and you have usually two flags C(arry), O(verflow), which you can use to see if your unsigned/signed addition overflowed. But some CPUs (my beloved M68000) had a special flag for that, namely eXtend (The "extend" (X) flag deserves special mention, because it is separate from the carry flag. This permits the extra bit from arithmetic, logic, and shift operations to be separated from the carry).

Now back our use case. Which should it be? I have no idea :). But since we have a different type for signed and unsigned arithmetic, it makes more sense to be like a overflow or extend. What you're proposing is more like overflow, what I'm proposing is more like extend, neither is carry :). Not sure which route is a "correct" one though.
Probably we should agree on one and use it consistently. I'm OK either way as long as it's consistent.
👍

@erik-3milabs
Copy link
Author

@lleoha thanks for the elaboration! I think we are (very close) to (finally 😄) being on the same page.

For unsigned (Uint) arithmetic, I propose we stick with carrying_neg; the carry "flag" would be set if and only if you're attempting to negate Uint::ZERO.

For signed (Int) arithmetic, I am ambivalent about naming the function extended_neg or overflowing_neg. Perhaps @tarcieri has a deciding opinion on this? Regardless of which is chosen, I completely agree that we should be consistent in using it! As long as the function's "flag" is set when attempting to negate Int::MIN, I'm all for it ;-)

@tarcieri tarcieri merged commit 04079c8 into RustCrypto:signed-int Nov 1, 2024
18 checks passed
@erik-3milabs erik-3milabs deleted the int-twos branch November 4, 2024 09:54
@erik-3milabs
Copy link
Author

@lleoha if you're interested to stay part of this project, you can have a look at the next PR that is open for review: #697

@lleoha
Copy link

lleoha commented Nov 4, 2024

@lleoha if you're interested to stay part of this project, you can have a look at the next PR that is open for review: #697

Sure, will do

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants